/*
 * Mostly taken from lbthomsen/esp-idf-littlevgl github.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "st7789.h"
#include "gpio_if.h"
#include "spi_if.h"
#include "gpio_types.h"
#include "esp_err.h"
#include "hdf_log.h"
#include "osal_time.h"
#include "driver/spi_common.h"
#include "driver/spi_master.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */
typedef struct {
    uint8_t cmd;
    uint8_t data[16];
    uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
} lcd_init_cmd_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void st7789_set_orientation(uint8_t orientation);

/**********************
 *  STATIC VARIABLES
 **********************/

static DevHandle spiHandle = NULL;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void st7789_init(void)
{
	
    lcd_init_cmd_t st7789_init_cmds[] = {
        {0xCF, {0x00, 0x83, 0X30}, 3},
        {0xED, {0x64, 0x03, 0X12, 0X81}, 4},
        {ST7789_PWCTRL2, {0x85, 0x01, 0x79}, 3},
        {0xCB, {0x39, 0x2C, 0x00, 0x34, 0x02}, 5},
        {0xF7, {0x20}, 1},
        {0xEA, {0x00, 0x00}, 2},
        {ST7789_LCMCTRL, {0x26}, 1},
        {ST7789_IDSET, {0x11}, 1},
        {ST7789_VCMOFSET, {0x35, 0x3E}, 2},
        {ST7789_CABCCTRL, {0xBE}, 1},
        {ST7789_MADCTL, {0x00}, 1}, // Set to 0x28 if your display is flipped
        {ST7789_COLMOD, {0x55}, 1},

#if ST7789_INVERT_COLORS == 1
		{ST7789_INVON, {0}, 0}, // set inverted mode
#else
 		{ST7789_INVOFF, {0}, 0}, // set non-inverted mode
#endif

        {ST7789_RGBCTRL, {0x00, 0x1B}, 2},
        {0xF2, {0x08}, 1},
        {ST7789_GAMSET, {0x01}, 1},
        {ST7789_PVGAMCTRL, {0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x32, 0x44, 0x42, 0x06, 0x0E, 0x12, 0x14, 0x17}, 14},
        {ST7789_NVGAMCTRL, {0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x31, 0x54, 0x47, 0x0E, 0x1C, 0x17, 0x1B, 0x1E}, 14},
        {ST7789_CASET, {0x00, 0x00, 0x00, 0xEF}, 4},
        {ST7789_RASET, {0x00, 0x00, 0x01, 0x3f}, 4},
        {ST7789_RAMWR, {0}, 0},
        {ST7789_GCTRL, {0x07}, 1},
        {0xB6, {0x0A, 0x82, 0x27, 0x00}, 4},
        {ST7789_SLPOUT, {0}, 0x80},
        {ST7789_DISPON, {0}, 0x80},
        {0, {0}, 0xff},
    };

    StartSPI();

	//Initialize non-SPI GPIOs
    (void)GpioSetDir(ST7789_DC, GPIO_DIR_OUT);

	HDF_LOGI("ST7789 Initialization.");

    //Send all the commands
    uint16_t cmd = 0;
    while (st7789_init_cmds[cmd].databytes!=0xff) {
        st7789_send_cmd(st7789_init_cmds[cmd].cmd);
        st7789_send_data(st7789_init_cmds[cmd].data, st7789_init_cmds[cmd].databytes&0x1F);
        if (st7789_init_cmds[cmd].databytes & 0x80) {
                vTaskDelay(100 / portTICK_RATE_MS);
        }
        cmd++;
    }

    st7789_set_orientation(CONFIG_LV_DISPLAY_ORIENTATION);
}

void StartSPI(void) {
    struct SpiDevInfo spiDevinfo = {0};
    spiDevinfo.busNum = VSPI_HOST;  //SPI设备总线号
    spiDevinfo.csNum = 0;           //SPI设备片选号
    spiHandle = SpiOpen(&spiDevinfo);
    if (spiHandle == NULL) {
        HDF_LOGE("SpiOpen: failed\n");
        return;
    }

}

void EndSPI(void) {

    if (spiHandle) {
        SpiClose(spiHandle);
        spiHandle = NULL;
    }
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

void st7789_send_cmd(uint8_t cmd)
{
    // disp_wait_for_pending_transactions();
    // gpio_set_level(ST7789_DC, 0);
    // disp_spi_send_data(&cmd, 1);

    struct SpiMsg msg = {0};
    msg.wbuf = &cmd;
    msg.len = 1;
    (void)GpioWrite(ST7789_DC, GPIO_VAL_LOW);
    (void)SpiTransfer(spiHandle, &msg, 1);
}

void st7789_send_data(void * data, uint16_t length)
{
    // disp_wait_for_pending_transactions();
    // gpio_set_level(ST7789_DC, 1);
    // disp_spi_send_data(data, length);
    if(length == 0) {
        data = NULL;
    }
    struct SpiMsg msg = {0};
    msg.wbuf = (uint8_t *)data;
    msg.len = length;
    (void)GpioWrite(ST7789_DC, GPIO_VAL_HIGH);
    (void)SpiTransfer(spiHandle, &msg, 1);
}

void st7789_send_color(void * data, uint16_t length)
{
    st7789_send_data(data, length);
}

static void st7789_set_orientation(uint8_t orientation)
{
    // ESP_ASSERT(orientation < 4);

    const char *orientation_str[] = {
        "PORTRAIT", "PORTRAIT_INVERTED", "LANDSCAPE", "LANDSCAPE_INVERTED"
    };

    HDF_LOGI("Display orientation: %s", orientation_str[orientation]);

    uint8_t data[] =
    {
#if CONFIG_LV_PREDEFINED_DISPLAY_TTGO
	0x60, 0xA0, 0x00, 0xC0
#else
	0xC0, 0x00, 0x60, 0xA0
#endif
    };

    HDF_LOGI("0x36 command value: 0x%02X", data[orientation]);

    st7789_send_cmd(ST7789_MADCTL);
    st7789_send_data((void *) &data[orientation], 1);
}
